#include "include/State.hpp"

State::State(verbosity v, const char *config)
{
	initialized = false;
	done = false;
	config_name = config;
	log_verbosity = v;
	
	m(LOG_VERBOSE, "Initializing State.\n");
	int ret = 0;
	LuaWrapper lconfig(this, config_name, config_name);
	ret += lconfig.getGlobalUShort("serverport", server_port);
	ret += lconfig.getGlobalString("serveraddress", server_address);
	ret += lconfig.getGlobalString("username", username);
	ret += lconfig.getGlobalString("password", password);
	
	if( ret )
	{
		m(LOG_ERROR, "Engine configuration file was incomplete or incorrect.  Aborting.\n");
		return;
	}
	
	initialized = true;
}

int State::run()
{
	int retval = 0, nbytes;
	char buff[sizeof(server_response)];
	action_data msg;
	server_response *resp;
	
	m(LOG_DEBUG, "Opening socket.\n");
	sockfd = socket(PF_INET, SOCK_STREAM, 0);
	if( sockfd == -1 )
	{
		// an error has ocurred
		m(LOG_ERROR, "socket(): %s\n", strerror(errno) );
		return 0;
	}
	
	dest_addr.sin_family = AF_INET; // host byte order
	dest_addr.sin_port = htons(server_port); // short, network byte order
	dest_addr.sin_addr.s_addr = inet_addr(server_address.c_str());
	memset(dest_addr.sin_zero, '\0', sizeof(dest_addr.sin_zero));
	
	m(LOG_GENERAL, "Connecting to %s on port %i.\n", server_address.c_str(), (int)server_port);
	if( connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) == -1 )
	{
		// something went wrong
		m(LOG_ERROR, "connect(): %s\n", strerror(errno));
		return 0;
	}
	
	// try authentication
	msg.action = CL_AUTHENTICATE;
	strncpy(msg.username, username.c_str(), 20);
	strncpy(msg.password, password.c_str(), 20);
	m(LOG_GENERAL, "Authenticating user %s...\n", username.c_str());
	m(LOG_DEBUG, "Sending authentication data.\n");
	send(sockfd, (void*)&msg, sizeof(msg), 0);
	m(LOG_DEBUG, "Waiting for response.\n");
	nbytes = recv(sockfd, &buff, sizeof(buff), 0);
	if(nbytes == 0)
	{
		// server disconnected
		m(LOG_ERROR, "recv(): Lost server connection.  Aborting.\n");
		return retval;
	} else if ( nbytes != sizeof(buff)) {
		m(LOG_ERROR, "recv(): Server response incomplete.\n");
		return retval;
	}
	
	resp = (server_response *) &buff;
	switch( resp->type )
	{
		case SV_SUCCESS:
			m(LOG_GENERAL, "Successfully authenticated user %s.\n", username.c_str());
			break;
			
		case SV_DISCONNECT:
			m(LOG_ERROR, "Server disconnected.\n");
			return retval;
			
		default:
			PrintServerMessage(resp->type, resp->previous_action, resp->error);
			return retval;
	} 
	
	// start readline thread
	int rc = pthread_create(&readline_thread, NULL, InputHandler, (void *)this);
	
	// loop until done
	while( !done)
	{
		nbytes = recv(sockfd, &buff, sizeof(buff), 0 );
		if( nbytes == 0 )
		{
			// server has disconnected.
			m(LOG_ERROR, "recv(): Lost connection to server.  Press return to exit.\n");
			done = true;
			continue;
		} else if( nbytes != sizeof(buff) ) {
			m(LOG_ERROR, "recv(): Incomplete server response.\n");
			continue;
		}
		
		resp = (server_response*)&buff;
		
		switch( resp->type )
		{
			case SV_MSG:
			// for safety's sake, append '\0' to the end of msg
			resp->msg[sizeof(resp->msg) - 1] = '\0';
			m(LOG_GENERAL, "\n%s\n", resp->msg );
			break;
			
			case SV_DISCONNECT:
			// server has disconnected
			m(LOG_GENERAL, "Server has disconnected us.  Press return to exit.\n");
			done = true;
			continue;
			
			default:
			PrintServerMessage(resp->type, resp->previous_action, resp->error);
			break;
		}
	}
	
	m(LOG_GENERAL, "Closing server connection.\n");
	// clean up
	close(sockfd);
	
	// wait for the user to close readline thread
	pthread_join(readline_thread, NULL);
	
	return retval;
}

void State::PrintServerMessage(response_types type, client_actions action, server_error error)
{
	std::string message;
	
	switch (action)
	{
		case CL_AUTHENTICATE:
		message = "Authentication: ";
		break;
		
		case CL_CREATE_ACCOUNT:
		message = "Account Creation: ";
		break;
		
		default:
		message = "Unknown action: ";
		break;
	}
	
	switch(type)
	{
		case SV_SUCCESS: 
		message = message + "Successfull.";
		m(LOG_GENERAL, "%s\n", message.c_str());
		break;
		
		case SV_ERROR:
		switch (error)
		{
			case ERR_NONE:
			message = message + "Successful.";
			break;

			case ERR_INTERNAL:
			message = message + "Server error.";
			break;

			case ERR_CORRUPT_CLIENT_MESSAGE:
			message = message + "Our message was corruped.";
			break;

			case ERR_USER_EXISTS:
			message = message + "User already exists.";
			break;

			case ERR_NONEXISTANT_USER:
			message = message + "User does not exist.";
			break;

			case ERR_EMAIL_IN_USE:
			message = message + "Email is already in use.";
			break;

			case ERR_INCORRECT_PASSWORD:
			message = message + "Password is incorrect.";
			break;

			default:
			message = message + "Unknown error code.";
			break;
		}
		m(LOG_ERROR, "%s\n", message.c_str());
		break;
	}
	
}

void State::PrintHelp()
{
	m(LOG_GENERAL, "Help.\n");
	m(LOG_GENERAL, "\tHelp strings will go here in the future.\n");
}

void *State::InputHandler ( void * data )
{
	State *st = (State *)data;
	char *line;
	std::string prompt;
	
	action_data msg;
	
	while( !st->done )
	{
		prompt = "client> ";
		line = readline(prompt.c_str());
		
		if( st->done )
		{
			free(line);
			continue;
		}
		
		if (line == (char *)NULL)
		{
			st->m(LOG_ERROR, "readline(): Something went wrong.  Probably memory allocation related. Aborting.\n");
			return 0;
		} else if (line[0] != '\0') {
			// /*echo the line*/ st->m(LOG_DEBUG, "%s%s\n", prompt.c_str(), line);
			add_history(line);
			
			// execute line
			if( line[0] != '\\' )
			{
				// treat it like a message
				msg.action = CL_SAY;
				strncpy(msg.msg,line, sizeof(msg.msg)-1);
				// for safety's sake append \0
				msg.msg[sizeof(msg.msg)-1] = '\0';
				int sent = send(st->sock(), (void *)&msg, sizeof( action_data ), 0);
				
				st->m(LOG_DEBUG, "Sent %i bytes with message %s\n", sent, msg.msg);
			} else {
				// it's a command to the client or server
				// process commands that go to the client
				if ( !strncmp(line, "\\exit", 5) )
				{
					st->m(LOG_DEBUG, "Disconnecting from server...\n");
					
					// send a disconnect to server
					msg.action = CL_DISCONNECT;
					strcpy(msg.msg, "Gotta go.");
					int sent = send(st->sock(), (void *)&msg, sizeof(action_data), 0);
					
					st->done = true;
					free(line);
					continue;
				} else if (!strncmp(line, "\\new", 4)) {
					msg.action = CL_CREATE_ACCOUNT;
					
					prompt = "username> ";
					free(line);
					line = readline(prompt.c_str());
					strncpy(msg.username, line, sizeof(msg.username));
					if( st->done )
					{
						free(line);
						continue;
					} else {
						free(line);
					}
					
					prompt = "email> ";
					line = readline(prompt.c_str());
					strncpy(msg.email, line, sizeof(msg.email));
					if( st->done )
					{
						free(line);
						continue;
					} else {
						free(line);
					}
					
					prompt = "password> ";
					line = readline(prompt.c_str());
					strncpy(msg.password, line, sizeof(msg.password));
					if( st->done )
					{
						free(line);
						continue;
					}
					
					int sent = send(st->sock(), (void *)&msg, sizeof(action_data), 0);
					
				}
				
				// process commands that go to the server
				
				
			}
			
		} else {
			//blank line == help
			st->m(LOG_DEBUG, "blank line == help");
			st->PrintHelp();
		}
		
		free(line);
	}
	
	return 0;
}

int State::m(verbosity verb, const char *fmt, ... )
{
	if( verb <= log_verbosity )
	{
		char tmp[TERMINAL_MAX_LINE_SIZE];
		va_list argp;
		va_start(argp, fmt);
		vsnprintf(tmp, TERMINAL_MAX_LINE_SIZE - 1, fmt, argp);
		va_end(argp);
		
		tmp[TERMINAL_MAX_LINE_SIZE-1] = '\0';
		
		if( verb == LOG_ERROR )
			fprintf(stderr, "Error: %s", tmp);
		else if ( verb == LOG_WARNING )
			fprintf(stderr, "Warning: %s", tmp);
		else
			fprintf(stderr, tmp);
		
		return 0;
	}
	
	return 1;
}

